﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Xml;
using UnityEngine;
using Warensoft.Unity.Communication.Client.Contracts;
using Warensoft.Unity.Communication.Client.DataClient;

namespace Warensoft.Unity.Communication.Client.Models
{
    /// <summary>
    /// Http请求协议包类
    /// Http request package class
    /// </summary>
    public partial class HttpRequest : IDisposable, IMessage
    {
        /// <summary>
        /// 设置或获取连接的地址
        /// get or set the server url
        /// </summary>
        public string Url { get; set; }

        internal Action<string> StringCallBack { get; set; }
        internal Action<XmlDocument> XMLCallBack { get; set; }
        internal Action<byte[]> BytesCallBack { get; set; }
        internal Action<HttpResponse> ResponseCallback { get; set; }
        /// <summary>
        /// 是否使用WWW类发送HTTP请求
        /// </summary>
        internal bool UseWWW { get; set; }
        /// <summary>
        /// 设置或获取本次请求的响应类型
        /// get or set the response type of the request
        /// </summary>
        public HttpResponseTypes ResponseType { get; set; }
        private string requestLine = "";
        /// <summary>
        /// 获取请求行
        /// get the request line
        /// </summary>
        public string RequestLine
        {
            get
            {
                return this.requestLine;
            }
        }
        private Dictionary<string, string> headers;
        /// <summary>
        /// 获取请求头
        /// get the request headers
        /// </summary>
        public Dictionary<string, string> Headers
        {
            get { return headers; }

        }
        /// <summary>
        /// 设置或获取要传输的数据流
        /// get or set the bytes to be sent
        /// </summary>
        public byte[] Data { set; get; }
        /// <summary>
        /// 设置或获取请求方法,默认方法是GET
        /// get or set the method of the request,the default method is GET
        /// </summary>
        public WebRequestMethods Method { get; set; }
        private SocketClient socketClient;
        protected bool finished;
        /// <summary>
        /// 指示本次请求是否完毕
        /// Wether the reqeust is finished
        /// </summary>
        public bool Finished
        {
            get { return finished; }
            set { finished = value; }
        }

        protected HttpResponse response = null;
        public bool IsSendingMessage { get; set; }
        /// <summary>
        /// 构造方法
        ///  <para>
        /// constructor
        /// </para> 
        /// </summary>
        public HttpRequest()
        {

            this.IsSendingMessage = false;
            this.finished = false;
            this.headers = new Dictionary<string, string>();
            this.requestLine = "{0} {1} HTTP/1.1";
            this.headers.Add("Content-Type", "application/x-www-form-urlencoded");
            this.headers.Add("Host", "");
            this.headers.Add("Content-Length", "0");
            CommunicationManager.SceneUpdate += SceneUpdate;
            this.socketClient = new SocketClient(typeof(HttpPackageManager));
            this.Data = new byte[0];

        }
        public HttpRequest(bool useWWW)
        {
            this.UseWWW = useWWW;
            if (!useWWW)
            {
                this.IsSendingMessage = false;
                this.finished = false;
                this.headers = new Dictionary<string, string>();
                this.requestLine = "{0} {1} HTTP/1.1";
                this.headers.Add("Content-Type", "application/x-www-form-urlencoded");
                this.headers.Add("Host", "");
                this.headers.Add("Content-Length", "0");
                CommunicationManager.SceneUpdate += SceneUpdate;
                this.socketClient = new SocketClient(typeof(HttpPackageManager));
                this.Data = new byte[0];
            }
            else
            {
                this.IsSendingMessage = false;
                this.finished = false;
                CommunicationManager.SceneUpdate += SceneUpdate;
                this.Data = new byte[0];
            }
        }
        public virtual void SceneUpdate(object sender, EventArgs e)
        {

        }
        /// <summary>
        /// 发送异步Http请求
        /// <para>
        /// send an asynchronus http request
        /// </para>
        /// </summary>
        /// <param name="responseCallback">
        /// 响应回调方法
        /// <para>
        /// response callback method
        /// </para>
        /// </param>
        public virtual void SendRequestAsync(Action<HttpResponse> responseCallback)
        {


            if (this.Url.ToLower().StartsWith("http://") == false)
            {
                throw new Exception("url must start width \"http://\"");
            }
            var paths = this.Url.Remove(0, 7).Split('/');
            var address = paths[0];
            string ipString = "";
            IPAddress ip;
            string portString = "";
            int port;
            if (address.Contains(':'))
            {
                ipString = address.Split(':')[0];
                portString = address.Split(':')[1];
            }
            else
            {
                ipString = address;
                portString = "80";
            }
            if (System.Text.RegularExpressions.Regex.IsMatch(ipString, @"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"))
            {
                ip = IPAddress.Parse(ipString);
            }
            else
            {
                try
                {
                    var addresses = Dns.GetHostAddresses(ipString);

                    ip = Dns.GetHostAddresses(ipString).First(item => item.AddressFamily == AddressFamily.InterNetwork);
                }
                catch (SocketException ex)
                {

                    throw ex;
                }
            }
            port = int.Parse(portString);
            this.socketClient.ConnectionStateChanged += (s, e) =>
            {
                if (this.socketClient.Connected)
                {
                    this.socketClient.SendMessage(this);
                }
            };
            this.socketClient.NewMessage += (s, e) =>
            {
                if (responseCallback != null)
                {
                    var args = e.Message as HttpResponse;
                    args.Request = this;
                    responseCallback(args);
                }
                this.IsSendingMessage = false;
                this.finished = true;
                this.socketClient.Disconnect();
            };

            this.socketClient.Connect(ip, port);


            this.IsSendingMessage = true;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public byte[] ToArray()
        {
            var paths = this.Url.Remove(0, 7).Split('/');
            var address = paths[0];

            var file = this.Url.Remove(0, 7).Replace(address, "");
            string method = "";
            switch (this.Method)
            {
                case WebRequestMethods.Post:
                    method = "POST";
                    break;
                case WebRequestMethods.Get:
                    method = "GET";
                    break;
                default:
                    break;
            }
            this.requestLine = string.Format(this.RequestLine, method, file);
            this.Headers["Host"] = address;
            this.Headers["Content-Length"] = this.Data.Length.ToString();

            MemoryStream ms = new MemoryStream();
            var headLineBuffer = System.Text.Encoding.UTF8.GetBytes(this.RequestLine + "\r\n");
            ms.Write(headLineBuffer, 0, headLineBuffer.Length);
            foreach (var header in this.Headers)
            {
                var headerBuffer = System.Text.Encoding.UTF8.GetBytes(header.Key + ":" + header.Value + "\r\n");
                ms.Write(headerBuffer, 0, headerBuffer.Length);
            }
            var whiteBuffer = System.Text.Encoding.UTF8.GetBytes("\r\n");
            ms.Write(whiteBuffer, 0, whiteBuffer.Length);
            ms.Write(this.Data, 0, this.Data.Length);
            var result = ms.ToArray();
            ms.Close();
            return result;
        }
        public void Dispose()
        {

        }
    }
    /// <summary>
    /// Unity3d 专用Http请求协议包类
    /// Http request package class,specified for Unity3d runtime,this class inherit from HttpReqeust class
    /// </summary>
    public class UnityHttpRequest : HttpRequest
    {
        WWW www;
        Action<HttpResponse> responseCallback;
        public UnityHttpRequest()
            : base()
        { }
        public UnityHttpRequest(bool useWWW)
            : base(useWWW)
        {

        }
        internal Action<Texture2D> ImageCallBack { get; set; }
        internal MovieTexture Movie
        {
            get
            {
                return this.www.movie;
            }
        }
        internal Action<MovieTexture> MovieTextureCallback { get; set; }
        private int initStep = 0;
        public override void SceneUpdate(object sender, EventArgs e)
        {
            if (this.initStep ==0)
            {
                if (this.www != null && this.www.movie.isReadyToPlay)
                {

                    if (responseCallback != null)
                    {
                        HttpResponse res = new HttpResponse();
                        res.status = 200;
                        res.Request = this;
                        responseCallback(res);
                    }
                    this.IsSendingMessage = false;
                    this.finished = true;
                    this.initStep = 1;
                    this.www.Dispose();
                    this.www = null;
                    GC.Collect();
                }
            }
            
            base.SceneUpdate(sender, e);
        }
        public override void SendRequestAsync(Action<HttpResponse> responseCallback)
        {
           
            if (!this.UseWWW)
            {
                base.SendRequestAsync(responseCallback);
            }
            else
            {
                 
                if (this.Url.ToLower().StartsWith("http://") == false)
                {
                    throw new Exception("url must start width \"http://\"");
                }
                
                www = new WWW(this.Url);
            
                this.responseCallback = responseCallback;
                 
                this.IsSendingMessage = true;
                
            }
        }
    }


}
